﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace CoolEngine.Engine.Graphics
{
    public class CoolModelPart
    {
        public int TriangleCount { get; private set; }
        public int VertexCount { get; private set; }
        public int VertexStride { get; private set; }
        private CoolModel model;
        public VertexDeclaration VertexDeclaration { get; private set; }
        public VertexBuffer VertexBuffer { get; private set; }
        public IndexBuffer IndexBuffer { get; private set; }
        public CoolModelPart Parent { get; private set; }
        //public Matrix Transform { get; private set; }
        public Bone Transform { get; private set; }
        private readonly List<CoolModelPart> children = new List<CoolModelPart>();
        private readonly ReadOnlyCollection<CoolModelPart> children_readonly;
        private GraphicsDevice device;
        public ReadOnlyCollection<CoolModelPart> Children { get { return children_readonly; } }
        public Effect Effect { get; private set; }
        public Matrix View { get; set; }
        public Matrix Projection
        {
            get
            {
                return projection;
            }
            set
            {
                projectionChanged = true;
                projection = value;
            }
        }
        private Matrix projection;
        private bool projectionChanged;
        private EffectParameter worldViewParameter, projectionParameter, timeNowParameter, timeLastParameter, nearFarClippingParameter, environmentCubeParameter;
        private Game game;
        public Matrix GetAccumulatedTransform()
        {
            if (Parent == null)
            {
                return model.Transform * Transform.GetAccumulatedTransform();
            }
            return Transform.GetAccumulatedTransform();
        }
        
        internal CoolModelPart(CoolModel model, ContentReader reader, Game game)
        {
            this.model = model;
            this.game = game;
            children_readonly = new ReadOnlyCollection<CoolModelPart>(children);
            device = game.GraphicsDevice;

            TriangleCount = reader.ReadInt32();
            VertexCount = reader.ReadInt32();
            VertexStride = reader.ReadInt32();
            VertexDeclaration = reader.ReadObject<VertexDeclaration>();
            VertexBuffer = reader.ReadObject<VertexBuffer>();
            IndexBuffer = reader.ReadObject<IndexBuffer>();
            reader.ReadSharedResource<Effect>(effect =>
            {
                this.Effect = effect;
                worldViewParameter = Effect.Parameters.GetParameterBySemantic("Sas.Camera.WorldToView");
                projectionParameter = Effect.Parameters.GetParameterBySemantic("Sas.Camera.Projection");
                timeLastParameter = Effect.Parameters.GetParameterBySemantic("Sas.Time.Last");
                timeNowParameter = Effect.Parameters.GetParameterBySemantic("Sas.Time.Now");
                nearFarClippingParameter = Effect.Parameters.GetParameterBySemantic("Sas.Camera.NearFarClipping");
                environmentCubeParameter = Effect.Parameters.GetParameterBySemantic("Sas.EnvironmentMap");
            });
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        internal void Draw(GameTime gameTime)
        {
            worldViewParameter.SetValue(GetAccumulatedTransform() * View);
            if (projectionChanged)
            {
                projectionParameter.SetValue(projection);
                projectionChanged = false;
            }
            if (timeNowParameter != null)
            {
                timeNowParameter.SetValue((float)gameTime.TotalRealTime.TotalMilliseconds);
            }
            if (timeLastParameter != null)
            {
                timeLastParameter.SetValue((float)(gameTime.TotalRealTime.TotalMilliseconds - gameTime.ElapsedRealTime.TotalMilliseconds));
            }

            device.VertexDeclaration = VertexDeclaration;
            device.Vertices[0].SetSource(VertexBuffer, 0, VertexStride);
            device.Indices = IndexBuffer;
            Effect.Begin();
            foreach (EffectPass pass in Effect.CurrentTechnique.Passes)
            {
                pass.Begin();
                device.DrawIndexedPrimitives(PrimitiveType.TriangleList,
                                        0, 0, VertexCount,
                                        0, TriangleCount);
                pass.End();
            }
            Effect.End();
        }
    }
}
